package com.yichuang.wuziqi.client;

import android.content.Context;
import android.graphics.Point;
import android.os.Bundle;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.widget.Toast;

import com.yichuang.wuziqi.config.Constants;
import com.yichuang.wuziqi.domain.Data;
import com.yichuang.wuziqi.game.Recorder;
import com.yichuang.wuziqi.player.Player;
import com.yichuang.wuziqi.tool.JsonTools;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;

/**
 * Created by xcm on 18-1-12.
 */

public class Client implements IClient {

    private static final String TAG = "xcm";
    private Socket socket;
    private BufferedWriter bw;
    private BufferedReader br;
    private boolean isReady = false;

    private boolean isGameOver = false;

    private MainHandler mainHandler;
    private WorkHandler workHandler;
    private HandlerThread handlerThread;

    private final int MESSAGE_CONNECT_SERVER = 1;
    private final int MESSAGE_GAME_MESSAGE = 2;
    private final int MESSAGE_INCOMING_MESSAGE = 3;

    private final int STATUS_ON_MY_TURN = 1;
    private final int STATUS_ON_OPPOSITE_TURN = 2;
    private final int STATUS_SERVER_CONNECT = 3;

    private Player player;
    private Context context;

    private GameListener gameListener;
    private Recorder recorder;

    private boolean myTurn = false;

    public Client(Context context, Player player) {
        this.player = player;
        this.context = context;
        mainHandler = new MainHandler();
        handlerThread = new HandlerThread("worker-thread");
        handlerThread.start();
        workHandler = new WorkHandler(handlerThread.getLooper());
        connectServer(Constants.SERVER_ADDRESS, Constants.SERVER_PORT);
    }

    public boolean readyForGame() {

        if (socket == null) {
            connectServer(Constants.SERVER_ADDRESS, Constants.SERVER_PORT);
            return false;
        }

        sendToServer(new Data(Data.PlayerAction.PLAYER_READY, ""));
        readFromServer();
        return true;
    }

    public boolean isReady() {
        return isReady;
    }

    private void reset() {
        isGameOver = false;
    }

    @Override
    public void connectServer(String host, int port) {

        Message message = Message.obtain();
        Bundle bundle = new Bundle();
        bundle.putInt("port", port);
        bundle.putString("host", host);
        message.setData(bundle);
        message.what = MESSAGE_CONNECT_SERVER;
        workHandler.sendMessage(message);
    }

    @Override
    public void sendToServer(Data data) {
        sendMessage(data);
    }

    @Override
    public void readFromServer() {
        new ReaderThread().start();
    }

    private void sendMessage(Data data) {
        Message message = Message.obtain();
        Bundle bundle = new Bundle();
        bundle.putSerializable("data", data);
        message.setData(bundle);
        message.what = MESSAGE_GAME_MESSAGE;
        boolean re = workHandler.sendMessage(message);
        Log.d(TAG, "sendMessage: " + re);
        Log.d(TAG, workHandler.hasMessages(MESSAGE_GAME_MESSAGE) + "");
    }

    class MainHandler extends android.os.Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case Data.ServerAction.GAME_START:
                    Toast.makeText(context, "游戏开始", Toast.LENGTH_SHORT).show();
                    if (gameListener != null) {
                        gameListener.onGameStatusChange(Data.ServerAction.GAME_START);
                    }
                    break;
                case Data.ServerAction.GAME_OVER:
                    Toast.makeText(context, "游戏结束", Toast.LENGTH_SHORT).show();
                    recorder.checkGameOver();
                    if (gameListener != null) {
                        gameListener.onGameStatusChange(Data.ServerAction.GAME_OVER);
                    }
                    break;
                case STATUS_ON_MY_TURN:
                    if (gameListener != null) {
                        gameListener.onTurn(1);
                    }
                    break;
                case STATUS_ON_OPPOSITE_TURN:
                    if (gameListener != null) {
                        gameListener.onTurn(0);
                    }
                    break;
            }
        }
    }

    /**
     * 负责向服务器发送数据和等待用户下子的线程
     */
    class WorkHandler extends android.os.Handler {

        BufferedWriter bw;

        public WorkHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MESSAGE_CONNECT_SERVER:
                    Bundle data = msg.getData();
                    String host = data.getString("host");
                    int port = data.getInt("port");
                    try {
                        socket = new Socket(host, port);
                    } catch (IOException e) {
                        e.printStackTrace();
                        isReady = false;
                    }
                    isReady = (socket != null);
                    Log.d("xcm", "连接服务器" + isReady);
                    break;
                case MESSAGE_GAME_MESSAGE:
                    if (isReady) {
                        sendToServer((Data) msg.getData().getSerializable("data"));
                    }
                    break;
                case MESSAGE_INCOMING_MESSAGE:
                    dealIncomingMessage(msg);
            }
        }

        /**
         * 处理服务器发过来的消息
         * 由子线程调用
         *
         * @param msg
         */
        private void dealIncomingMessage(Message msg) {
            //由于等待选手处理,可能会阻塞,因此不能在主线城处理
            Data data = JsonTools.toOject(msg.getData().getString("data"), Data.class);
            if (data == null) {
                return;
            }
            //游戏开始
            if (data.getType() == Data.ServerAction.GAME_START) {
                player.setSide(Integer.valueOf(data.getData()));
                mainHandler.sendEmptyMessage(Data.ServerAction.GAME_START);
                //如果是我方先手，则需要下棋
                if (player.getSide() == Player.WHITE_SIDE) {
                    mainHandler.sendEmptyMessage(STATUS_ON_MY_TURN);
                    waitForPlayerStep(data);
                }
                //游戏结束
            } else if (data.getType() == Data.ServerAction.GAME_OVER) {
                mainHandler.sendEmptyMessage(Data.ServerAction.GAME_OVER);
                isGameOver = true;
            } else if (data.getType() == Data.PlayerAction.PLAYER_DROP) {
                //记录对面的下子
                int playerSide = player.getSide();
                Point p = JsonTools.toOject(data.getData(), Point.class);
                recorder.record((++playerSide) % 2, p);
                Log.d(TAG, "对方选手下棋：" + p);
                mainHandler.sendEmptyMessage(STATUS_ON_MY_TURN);
                waitForPlayerStep(data);
            }
        }

        /**
         * 处理本地用户做出的反应
         *
         * @param data todo 新开一个线程来处理
         */
        private void waitForPlayerStep(final Data data) {
            new Thread() {
                @Override
                public void run() {
                    Data step = player.oneStep(data);
                    switch (step.getType()) {
                        case Data.PlayerAction.PLAYER_DROP:
                            //记录我方下子
                            Point p = JsonTools.toOject(step.getData(), Point.class);
                            Log.d(TAG, "本地选手下棋：" + p);
                            recorder.record(player.getSide(), p);
                            break;
                    }
                    mainHandler.sendEmptyMessage(STATUS_ON_OPPOSITE_TURN);
                    sendToServer(step);
                }
            }.start();
        }

        public void sendToServer(Data data) {
            try {
                if (bw == null) {
                    bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
                }
                bw.write(JsonTools.toJSONString(data));
                bw.write("\n");
                Log.d("xcm", "发送给服务器：" + data.toString());
                bw.flush();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 负责从服务器读取数据的线程
     */
    class ReaderThread extends Thread {
        BufferedReader reader;

        @Override
        public void run() {
            while (!isGameOver) {
                try {
                    if (reader == null) {
                        reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                    }
                    String input = reader.readLine();
                    Log.d("ReaderThread", "受到来自服务器的消息：" + input);
                    if (input != null) {
                        Message message = Message.obtain();
                        message.what = MESSAGE_INCOMING_MESSAGE;
                        Bundle bundle = new Bundle();
                        bundle.putString("data", input.trim());
                        message.setData(bundle);
                        workHandler.sendMessage(message);
                    } else {
                        Log.e(TAG, "服务器连接已断开");
                        socket = null;
                        break;
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                    break;
                }
            }
        }
    }

    public interface GameListener {
        /**
         * 游戏状态改变时的回调
         *
         * @param status
         */
        void onGameStatusChange(int status);

        /**
         * 当轮到玩家下棋时的回调
         *
         * @param side 1代表我方下棋，0代表对方下棋
         */
        void onTurn(int side);
    }

    public void setGameListener(GameListener gameListener) {
        this.gameListener = gameListener;
    }

    public void setRecorder(Recorder recorder) {
        this.recorder = recorder;
    }

    public void setGameOver(boolean gameOver) {
        isGameOver = gameOver;
        handlerThread.quit();
    }
}
